﻿using DG.Tweening;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Options;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using AudioStudio;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.UI;
using XLua;
using Yoozoo.Core.Extensions.Engine;
using Yoozoo.Framework.Core;
using Yoozoo.Managers.ResourceManagerV2.Runtime;
using Yoozoo.Mars.Got.SpriteAnim;
using Yoozoo.Modules.InnerCity;
using Random = UnityEngine.Random;

namespace Yoozoo.Gta.Common
{
    public enum SpriteResType
    {
        Food = 1,
        Iron = 2,
        Stone = 3,
        Wood = 4,
        Coin = 5,
        Gold = 6,
    }

    public class ResFlyInfo
    {
        // 第一段飞行速度
        private const float ResSpeed1 = 80;

        // 第二段飞行速度
        private const float ResSpeed2 = 300;

        // 飞行间隔
        private const float FlyInterval = 0.05f;

        // 震动幅度
        private static readonly Vector3 ShakeScale = new Vector3(1.25f, 1.25f, 1f);

        // 震动时间
        private const float ShakeTime = 0.05f;

        private static readonly Vector3 OutPos = new Vector3(5000, 5000, 5000);

        private SpriteParticleSystemManager _systemManager;
        private AnimSprite[] _resSprites;
        private Vector3 _startPos;
        private Vector3 _endPos;
        private float _time;
        private AnimSpriteCreator _creator;
        private int _animId;
        private Action<int> _action;
        private TweenerCore<Vector3, Vector3, VectorOptions> _stepOneTween;
        private int _playCount = 0;
        private List<Tween> _tweens = new List<Tween>();
        private Vector3[] _targetPoses;
        private List<GameObject> flyObjs;
        private Transform shakeTransform;
        private Tween shakeTween;
        private CommonSpriteManager mgr;
        private LuaFunction onOneCompleted;
        private LuaFunction onAllCompleted;

        private IEnumerator _enumerator;
        
        public ResFlyInfo(AnimSpriteCreator creator, SpriteParticleSystemManager systemManager, int animId,
            Action<int> action)
        {
            _creator = creator;
            _systemManager = systemManager;
            _animId = animId;
            _action = action;
        }

        public ResFlyInfo(CommonSpriteManager mgr, int animId, Action<int> action)
        {
            this.mgr = mgr;
            this._animId = animId;
            this._action = action;
        }

        public void Init(int resType, int count, Vector3 startPos, Vector3 endPos, float time, RectTransform rt,
            Camera uiCamera, Camera cityCam, LuaFunction onOneCompleted = null, LuaFunction onAllCompleted = null)
        {
            this.flyObjs = FlyAnimationMgr.GetInstance(nameof(FlyAnimationMgr)).GetResFlyObjs(resType, count);
            var mainCameraPos = Vector3.zero;
            if (cityCam != null)
                mainCameraPos = cityCam.WorldToScreenPoint(startPos);
            else if (WorldCameraManager.mainCamera != null)
                mainCameraPos = WorldCameraManager.mainCamera.WorldToScreenPoint(startPos);
            
            RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, mainCameraPos,
                uiCamera, out this._startPos);

            this._endPos = endPos;
            this._time = time;
            this.shakeTransform = rt;
            this.onOneCompleted = onOneCompleted;
            this.onAllCompleted = onAllCompleted;
        }

        public void StartPlay()
        {
            foreach (var sprite in _resSprites)
            {
                sprite.rotation = Vector3.left;
                sprite.spriteSize = 100;
                _systemManager.PlayActionAnimation(sprite, SPRITE_ACTION_ENUM.WALK);
                FlyAnimStepOne();
            }
        }

        public void StartPlay2(float angleMin, float angleMax, float disMin, float disMax)
        {
            if (this.flyObjs == null)
                return;

            this._playCount = 0;
            _enumerator = _StartPlay2(angleMin, angleMax, disMin, disMax);
            this.mgr.StartCoroutine(_enumerator);
        }

        private IEnumerator _StartPlay2(float angleMin, float angleMax, float disMin, float disMax)
        {
            var index = 0;
            var startPos = _startPos;
            foreach (var obj in this.flyObjs)
            {
                FlyAniStepOne2(obj, startPos, ref angleMin, ref angleMax, ref disMin, ref disMax, index++);
                yield return new WaitForSeconds(FlyInterval);
            }
            _enumerator = null;
        }

        private void FlyAnimStepOne()
        {
            var angle = 10;
            Sequence s = DOTween.Sequence();
            s.PrependInterval(0.1f);
            _targetPoses = new Vector3[_resSprites.Length];
            for (int i = 0; i < _resSprites.Length; i++)
            {
                var finalAngle = angle + i * Random.Range(20, 50);
                var end = new Vector3(_startPos.x - Mathf.Sin(finalAngle) * +Random.Range(60, 200),
                    _startPos.y - Mathf.Cos(finalAngle) * Random.Range(60, 200), _startPos.z);
                _targetPoses[i] = end;
                var i1 = i;
                var tween = DOTween.To(() => _startPos, (value) => { _resSprites[i1].position = value; }, end,
                    1.2f).SetEase(Ease.Linear);
                s.Join(tween);
            }

            s.AppendCallback(FlyAnimStepTwo);
        }

        private void FlyAniStepOne2(GameObject flyObj, Vector3 startPos, ref float angleMin, ref float angleMax,
            ref float disMin, ref float disMax, int index)
        {
            flyObj.transform.position = startPos;
            flyObj.transform.eulerAngles = new Vector3(-30, 0, 0);
            var angle = Random.Range(angleMin, angleMax);
            if (angle > 360)
                angle -= 360;

            // 转到正规的角度
            var halfMax = angleMax * 0.5f;
            if (angle <= halfMax)
            {
                angle = 90 + (halfMax - angle);
            }
            else
            {
                angle = angle - halfMax;
                if (angle <= 90)
                    angle = 90 - angle;
                else
                    angle = 360 - (angle - 90);
            }

            var rad = angle * Mathf.Deg2Rad;
            var dis = /*(disMin + disMax) / 2;*/Random.Range(disMin, disMax);
            var end = new Vector3(startPos.x + Mathf.Round(Mathf.Cos(rad) * dis),
                startPos.y + Mathf.Round(Mathf.Sin(rad) * dis), startPos.z);
            var oriScale = flyObj.transform.localScale;
            flyObj.transform.localScale = Vector3.zero;
            Sequence mySequence = DOTween.Sequence();
            mySequence.Append(DOTween.To(() => flyObj.transform.position,
                    (value) => { flyObj.transform.position = value; }, end,
                    dis / ResSpeed1 /* + index * FlyInterval*/).SetEase(Ease.OutSine))
                .AppendCallback(() => { dis = Vector3.Distance(flyObj.transform.position, this._endPos); })
                //.AppendInterval(index * FlyInterval)
                .Append(DOTween.To(() => flyObj.transform.position, (value) => { flyObj.transform.position = value; },
                    this._endPos, dis / ResSpeed2).SetEase(Ease.InSine))
                .AppendCallback(() =>
                {
                    if (this.shakeTween != null && this.shakeTween.IsActive() && this.shakeTween.IsPlaying())
                    {
                        this.shakeTween.Kill();
                        this.shakeTransform.localScale = Vector3.one;
                    }

                    this.shakeTween = this.shakeTransform.DOScale(ShakeScale, ShakeTime).SetEase(Ease.Linear)
                        .OnComplete(() =>
                        {
                            this.shakeTween = this.shakeTransform.DOScale(Vector3.one, ShakeTime).SetEase(Ease.Linear);
                            //AudioManager.PlayVoice("am_resource_ac");
                        });

                    flyObj.transform.position = OutPos;
                    this.onOneCompleted?.Action(true);
                    if (++_playCount == this.flyObjs.Count)
                    {
                        _action?.Invoke(_animId);
                        this.onAllCompleted?.Action(true);
                    }
                }).Insert(0, flyObj.transform.DOScale(oriScale, 0.15f));
            _tweens.Add(mySequence);
        }

        private void FlyAnimStepTwo()
        {
            for (int i = 0; i < _targetPoses.Length; i++)
            {
                FlyAnimStepTwo(_targetPoses[i], i);
            }
        }

        private void FlyAnimStepTwo(Vector3 startPos, int i1)
        {
            Tween t = DOTween.To(() => startPos, (value) => { _resSprites[i1].position = value; }, _endPos, 2f)
                .SetAutoKill(false).OnComplete(() =>
                {
                    _playCount++;
                    _action?.Invoke(_animId);
                });
            _tweens.Add(t);
        }

        public void DestroySprite()
        {
            foreach (var tween in _tweens)
            {
                tween.Kill();
            }

            _tweens.Clear();
            foreach (var sprite in _resSprites)
            {
                _systemManager.RemoveSprite(sprite);
            }

            _systemManager = null;
            _resSprites = null;
            _creator = null;
        }

        public void KillTween()
        {
            if (_tweens!=null)
            {
                foreach (var tween in _tweens)
                {
                    tween?.Kill();
                }
                _tweens.Clear();
            }
        }
        public void DestroyObjs()
        {
            if (_enumerator!=null)
            {
                this.mgr.StopCoroutine(_enumerator);
                _enumerator = null;
            }
            
            if (flyObjs!=null)
            {
                foreach (var obj in flyObjs)
                {
                    if (obj)
                    {
                        GameObject.Destroy(obj);
                    }
                }
                flyObjs.Clear();
            }

        }
    }

    public class SoldierFly
    {
        private float minAngle;
        private float maxAngle;
        private float minDis;
        private float maxDis;
        private float speedStage1;
        private float speedStage2;

        private Action<Transform> callback;

        public void InitArg(float minAngle, float maxAngle, float minDis, float maxDis, float speedStage1,
            float speedStage2)
        {
            this.minAngle = minAngle;
            this.maxAngle = maxAngle;
            this.minDis = minDis;
            this.maxDis = maxDis;
            this.speedStage1 = speedStage1;
            this.speedStage2 = speedStage2;
        }

        public void PlayAni(Vector3 startPos, RectTransform flyTo, Transform flyObj, Camera uiCam,
            GameObject flash, GameObject tail, Action onPlayEnd, Camera cityCam,Camera hudCamera, bool isUIPos = false)
        {
            var startScreenPos = hudCamera.WorldToScreenPoint(startPos);
            //startScreenPos.z = 0;

            Vector2 viewport = new Vector2(startScreenPos.x / (1.0f * Screen.width), startScreenPos.y / (1.0f * Screen.height));

            var startWorldUIPos = CameraUtils.ViewportToUIWorld(viewport);

            var endScreenPos = uiCam.WorldToScreenPoint(flyTo.position);
            endScreenPos.z = startScreenPos.z;
            var e = flyTo.position;//uiCam.ScreenToWorldPoint(endScreenPos);
            e.z = 0;


            if (flash != null)
            {
                flash.transform.position = startPos;
                flash.SetActive(true);
            }

            flyObj.SetActive(true);

            if (tail != null)
            {
                tail.SetActive(true);
                var t = tail.transform;
                startWorldUIPos.z = 0;
                //startWorldUIPos.y -= 10;
                t.localPosition = startWorldUIPos;
                t.DOMove(e, this.speedStage2).SetEase(Ease.Linear)
                    .OnComplete(
                        () => { onPlayEnd?.Invoke(); });
            }
        }
    }

    public class CommonSpriteManager : SingletonMono<CommonSpriteManager>
    {
        private static CommonSpriteManager _instance;
        private SpriteParticleSystemManager _systemManager;
        private AnimSpriteCreator _creator;
        private AnimSprite[] _resSprites;
        private Vector3 _startPos;
        private Vector3 _endPos;
        private float _time;
        private int animId = 0;
        private Dictionary<int, ResFlyInfo> resInfos = new Dictionary<int, ResFlyInfo>();

        private int minAngle;
        private int maxAngle;
        private int minDisRatio;
        private int maxDisRatio;
        private int gridDis;

        public void Init()
        {
            _systemManager = CityUnitMgr.Instance.ParticleSystemManager;
            LoadSpriteRes();
        }

        public void Init2(int minAngle, int maxAngle, int minDisRatio, int maxDisRatio, int gridDis)
        {
            this.minAngle = minAngle;
            this.maxAngle = maxAngle;
            this.minDisRatio = minDisRatio;
            this.maxDisRatio = maxDisRatio;
            this.gridDis = gridDis;
            _systemManager = CityUnitMgr.Instance.ParticleSystemManager;
        }

        public void Clear()
        {
            if (resInfos!=null)
            {
                foreach (var resInfo in resInfos)
                {
                    if (resInfo.Value!=null)
                    {
                        resInfo.Value.KillTween();
                        resInfo.Value.DestroyObjs();
                    }
                }
                resInfos.Clear();
            }   
        }

        public void LoadSpriteRes()
        {
            _systemManager.CreateSpriteCreator("world_march_resource", "ResFly", "Sprites/Default", OnLoadEnd, 5);
        }

        public void flyAnim(int resType, int count, Vector3 startPos, Vector3 endPos, float time,
            RectTransform rectrans, Camera uiCamera, Camera cityCam)
        {
            var info = new ResFlyInfo(_creator, _systemManager, animId, OnPlayEnd);
            info.Init(resType, count, startPos, endPos, time, rectrans, uiCamera, cityCam);
            resInfos.Add(animId, info);
            animId++;

            info.StartPlay();
        }

        public void FlyAnim2(int resType, int count, Vector3 startPos, Vector3 endPos, float time,
            float gridRadius, RectTransform rt, Camera uiCamera, LuaFunction onOneFlyCompleted,
            LuaFunction onFlyCompleted, Camera cityCam)
        {
            var info = new ResFlyInfo(this, animId, OnPlayEnd);
            info.Init(resType, count, startPos, endPos, time, rt, uiCamera, cityCam, onOneFlyCompleted, onFlyCompleted);
            resInfos.Add(animId, info);
            animId++;

            var len = gridRadius * this.gridDis;
            info.StartPlay2(this.minAngle, this.maxAngle, len * this.minDisRatio, len * this.maxDisRatio);
        }
        
        public void FlySoldierAni(GameObject go, RectTransform powerRect, Vector3 startPos,
            float radius, Camera uiCam, LuaFunction callback, bool isUIPos, Camera cityCam, Camera hudCamera)
        {
            if (go == null)
                return;

            var _go = GameObject.Instantiate(go);
            Canvas nwCanvas = _go.AddComponent<Canvas>();
            
            var t = _go.transform;
            var t1 = go.transform;
            t.rotation = t1.rotation;
            t.localScale = t1.lossyScale;
            go = _go;
            _go.SetActive(true);
            nwCanvas.renderMode = RenderMode.ScreenSpaceCamera;
            nwCanvas.worldCamera = hudCamera;
            GameObject uiRoot = GameObject.Find("RootCanvas");
            var loader = ResLoader.Alloc();
            GameObject flash = null;
            GameObject tail = null;
            loader.Add2Load("Assets/ResourcesAssets/Prefabs/Effect/UI/sfx_fly_trail_03.prefab",
                (success, assetName, asset) =>
                {
                    if (success)
                    {
                        tail = GameObject.Instantiate(asset as GameObject, uiRoot.transform);
                        tail.SetActive(false);
                    }
                });

            
            loader.Add2Load("Assets/ResourcesAssets/Prefabs/Effect/UI/sfx_ui_glow_01.prefab",
                (success, assetName, asset) =>
                {
                    if (success)
                    {
                        flash = GameObject.Instantiate(asset as GameObject);
                        flash.SetActive(false);
                    }
                });

            loader.Load(() =>
            {
                _FlySoldierAni(go, powerRect, startPos, radius, uiCam, flash, tail, callback, cityCam, hudCamera, isUIPos);
            });
        }

        private void _FlySoldierAni(GameObject flyObj, RectTransform powerRect, Vector3 startPos,
            float radius, Camera uiCam, GameObject flash, GameObject tail, LuaFunction callback, Camera cityCam,Camera hudCamera, bool isUIPos = false)
        {
            var flyTask = new SoldierFly();
            var args = FlyAnimationMgr.GetInstance("FlyAnimationMgr").GetSoldierFlyArg();
            flyTask.InitArg(args.minAngle, args.maxAngle, args.minDis, args.maxDis, args.speedStage1, args.speedStage2);
            flyTask.PlayAni(startPos, powerRect, flyObj.transform, uiCam, flash, tail, () =>
            {
                Destroy(flyObj);
                if (flash != null)
                    Destroy(flash);

                if (tail != null)
                    Destroy(tail);

                callback?.Action(-1);
            }, cityCam, hudCamera, isUIPos);
        }

        public void OnPlayEnd(int id)
        {
            resInfos[id].DestroyObjs();
            resInfos.Remove(id);
        }

        private void OnLoadEnd(AnimSpriteCreator creator)
        {
            _creator = creator;
            _systemManager.SetRendererPropertyBlock(creator, new MaterialPropertyBlock());
        }
    }
}